<template>
  <div class="menu-container">
    <!-- 返显 1. 卡片，新增顶级菜单按钮 和 展示树  -->
    <!-- 菜单编辑 1. 增加触发 编辑 事件 -->
    <el-card>
      <template #header>
        <el-button @click="handleCreateRootMenu">新增顶级菜单</el-button>
      </template>
      <div class="menu-tree">
        <el-tree
          :data="menus"
          :props="defaultProps"
          :expand-on-click-node="false"
          highlight-current
          draggable
          :allow-drop="allowDrop"
          :allow-drag="allowDrag"
          @node-drop="handleNodeDrop"
          @node-click="handleNodeClick"
        >
          <template #default="{ node, data }">
            <!--  <div v-if="false">{{ node }}</div> 避免报错， 没有实际意义 -->
            <div v-if="false">{{ node }}</div>
            <p class="custom-item">
              <span>{{ data.title }} </span>
              <span
                ><el-button link @click="handleCreateChildMenu(data)"
                  >添加</el-button
                >
                <el-button link @click="handleRemoveMenu(data)"
                  >删除</el-button
                ></span
              >
            </p>
          </template>
        </el-tree>
      </div>
    </el-card>
    <!-- 菜单编辑 2. 实现菜单编辑界面 -->
    <el-card class="edit-card">
      <template #header> 编辑菜单 </template>
      <editor-menu
        v-show="editData && editData.id"
        :data="editData!"
        @updateEdit="handleUpdateEdit"
      />
      <span v-if="editData == null">从菜单列表选择一项后，进行编辑</span>
    </el-card>
    <!-- html 2. 增加菜单组件 -->
    <right-panel v-model="panelVisible" :title="panelTitle" :size="330">
      <add-menu @submit="submitMenuForm"></add-menu>
    </right-panel>
  </div>
</template>

<script lang="ts" setup>
import { MenuData } from "@/api/menu"
// 刷新页面 1. 导入 useReloadPage
import { useReloadPage } from "@/hooks/useReloadPage"
// 返显 2. 导入 useMenuStore
import { ITreeItemData, useMenuStore } from "@/stores/menu"
// 拖拽 1. 导入 节点 类型
import type Node from "element-plus/es/components/tree/src/model/node"

// 拖拽 2. 拖拽节点，顶级节点可以拖拽， 需要使用上方 节点 类型
const allowDrag = (draggingNode: Node) => {
  return (
    draggingNode.data.parent_id === 0 || draggingNode.data.parent_id === null
  )
}
// 拖拽时判定目标节点能否成为拖动目标位置。 如果返回 false ，拖动节点不能被 拖放到目标节点。
// type 参数有三种情况：'prev'、'inner' 和 'next'，分别表示放置在目标节点前、插入至目标节点和放置在目标节点后

// 拖拽 3. 声明 插入位置 枚举
type DropType = "prev" | "inner" | "next"
// 拖拽 4. 控制 插入位置 枚举， 需要使用上方 插入位置 类型
const allowDrop = (
  // 表示被拖拽的节点
  draggingNode: Node,
  // 目标节点
  dropNode: Node,
  // 放置位置
  type: DropType
) => {
  // 根节点，不能插入至目标节点内
  if (
    draggingNode.data.parent_id === 0 ||
    draggingNode.data.parent_id == null
  ) {
    // 根节点，不能插入至目标节点内
    return type !== "inner"
  }
}
// 拖拽 5. 拖拽结束后，批量更新菜单
const handleNodeDrop = () => {
  store.updateBulkMenu()
}
// 刷新页面 2. 使用 useReloadPage
const { reloadPage } = useReloadPage()
// 返显 3. 使用 useMenuStore
const store = useMenuStore()
// 返显 4. 获取 菜单树
const menus = computed(() => store.state.menuTreeData)
store.getAllMenuList()
// el-tree 属性
const defaultProps = {
  children: "children",
  label: "title"
}
// 增加菜单 2. 声明节点类型变量 0 是顶级菜单 1 是子级菜单
const menuType = ref(0) //
// 增加菜单 3. 抽屉弹窗控制变量
const panelVisible = ref(false)
// 增加菜单 4. 抽屉弹窗标题
const panelTitle = computed(() => {
  return menuType.value === 0 ? "添加顶级节点" : "添加子节点"
})
// 增加菜单 5. 新增顶级菜单弹窗 操作函数
const handleCreateRootMenu = () => {
  menuType.value = 0
  panelVisible.value = true
}
// 增加菜单 6. 声明子级的父级数据变量
const parentData = ref<ITreeItemData | null>()
// 增加菜单 7. 新增子级菜单弹窗 操作函数
const handleCreateChildMenu = (data: ITreeItemData) => {
  // 设置编辑类型 1 子级菜单
  menuType.value = 1
  // 展示弹窗
  panelVisible.value = true
  // 父节点数据
  parentData.value = data
}
// 重置方法
const resetStatus = () => {
  panelVisible.value = false
  parentData.value = null
  // 刷新页面
  reloadPage()
}
// 增加菜单 8-1. 生成 菜单 sort_id
// 顶级节点 传 树形结构数组，子级节点传他的父级节点
const genMenuSortId = (list: ITreeItemData[]) => {
  if (list && list.length) {
    // 新增根节点是根据 最后一个的父节点 sort_id + 1 来计算的
    return list[list.length - 1].sort_id + 1
  }
  return 0
}
// 增加菜单 8. 添加顶级菜单，添加菜单的核心是parent_id , sord_id
const handleAddRootMenu = async (data: MenuData) => {
  // 父级id
  data.parent_id = 0
  // 增加菜单 8-1. 生成 菜单 sort_id
  // 根节点是根据 所有的父节点来计算的， 儿子是根据爸爸有几个儿子来算的
  data.sort_id = genMenuSortId(menus.value)
  // 8-2. 执行增加菜单接口
  let res = await store.appendMenu(data)
  if (res) {
    proxy?.$message.success("根菜单添加成功了")
  }
}
const { proxy } = getCurrentInstance()!
// 9-1. 给新子节点数据 增加 sort_id parent_id
const genChild = (data: MenuData) => {
  // 9-1-1. 获取父级数据 !表示一定有
  const parent = parentData.value!
  // 9-1-2. 父级有无子节点，给数组形式
  if (!parent.children) {
    parent.children = []
  }
  // 9-1-3. 生成 sort_id
  data.sort_id = genMenuSortId(parent.children)
  // 9-1-4. 生成 parent_id
  data.parent_id = parent.id
  return data
}
// 增加菜单 9. 添加子级菜单，添加菜单的核心是parent_id , sord_id
const handleChildRootMenu = async (data: MenuData) => {
  // 9-1. 给新子节点数据 增加 sort_id parent_id
  const child = genChild(data)
  // 9-2. 增加菜单
  let res = await store.appendMenu(child)
  if (res) {
    proxy?.$message.success("子菜单添加成功了")
  }
}
// 增加菜单 2. 增加菜单提交函数
const submitMenuForm = async (data: MenuData) => {
  if (menuType.value === 0) {
    // 添加根节点
    handleAddRootMenu({ ...data })
  } else {
    // 添加子节点
    handleChildRootMenu({ ...data })
  }
  resetStatus()
}
const handleRemoveMenu = async (data: MenuData) => {
  try {
    await proxy?.$confirm("你确定删除" + data.title + "菜单吗?", {
      type: "warning"
    })
    await store.removeMenu({
      ...data
    })
    proxy?.$message.success("菜单删除成功")
    // 刷新页面
    reloadPage()
  } catch {
    proxy?.$message.info("取消菜单")
  }
}
// 菜单编辑 1-1. 点击el-tree, 编辑菜单数据
const editData = ref({} as MenuData)
// 菜单编辑 1-2. 编辑菜单事件
const handleNodeClick = (data: MenuData) => {
  editData.value = { ...data }
}
// 菜单编辑 3. 提交菜单编辑
const handleUpdateEdit = async (data: Partial<MenuData>) => {
  const r = await store.updateMenu(data)
  if (r) {
    proxy?.$message.success("菜单编辑成功")
    reloadPage()
  }
}
</script>

<style scoped lang="scss">
.menu-container {
  @apply flex p-20px;
}
.menu-tree {
  @apply min-w-500px h-400px overflow-y-scroll;
}
.custom-item {
  @apply flex items-center justify-between flex-1;
}
.edit-card {
  @apply flex-1 ml-15px;
}
</style>
